﻿namespace Sai.Core.Rules
{
	using Sai.Core.Introspection;
	using System.Linq;

	/// <summary>
	/// Rule used to check properties in a class declaration. 
	/// Checks for obsolete properties and properties removed from second version.
	/// </summary>
    public class CheckTypeProperties : IReferenceTypeRule
	{
		/// <summary>
		/// Validation for this rule. Feeds changes found to type difference object.
		/// </summary>
		/// <param name="was">The old version.</param>
		/// <param name="isNow">The new version.</param>
		/// <param name="typeDifference">The type difference.</param>
        public void Validate(ReferenceTypeDeclaration was, ReferenceTypeDeclaration isNow, ITypeDifference typeDifference)
		{
			if (was.Properties.Count == 0 && isNow.Properties.Count == 0)
			{
				return;
			}

            var obsolete = isNow.Properties.FindObsoleteMembers();

            foreach (var obs in obsolete)
            {
                typeDifference.IdentifiedChange(
                    new UnaryTypeChange(
                        TypeChangeReason.PropertyObsolete,
                        obs
                    ));
            }

            // check that properties removed
            var removed = isNow.Properties.FindRemovedMembers(was.Properties);

            foreach (var remove in removed)
            {
                typeDifference.IdentifiedChange(
                    new UnaryTypeChange(
                        TypeChangeReason.PropertyRemoved,
                        remove
                    ));
            }

            // check properties added
            var added = isNow.Properties.FindAddedMembers(was.Properties);

            foreach (var add in added)
            {
                typeDifference.IdentifiedChange(
                    new UnaryTypeChange(
                        TypeChangeReason.PropertyAdded,
                        add
                    ));
            }

            var commonProperties = isNow.Properties.FindMatchingMembers(was.Properties);

			// look at common properties
            if (commonProperties.Any())
            {
                foreach (var property in commonProperties)
                {
                    PropertyDeclaration oldProperty = property.Item1;
                    PropertyDeclaration newProperty = property.Item2;

                    if (oldProperty != null && newProperty != null)
                    {
                        if (oldProperty.HasPublicGetter && !newProperty.HasPublicGetter)
                        {
                            typeDifference.IdentifiedChange(
                                new BinaryTypeChange(
                                    TypeChangeReason.PropertyChanged,
                                    oldProperty,
                                    newProperty,
                                    "Get removed"
                                ));
                        }

                        if (oldProperty.HasPublicSetter && !newProperty.HasPublicSetter)
                        {
                            typeDifference.IdentifiedChange(
                                new BinaryTypeChange(
                                    TypeChangeReason.PropertyChanged,
                                    oldProperty,
                                    newProperty,
                                    "Set removed"
                                ));
                        }

                        if (oldProperty.TypeName != newProperty.TypeName)
                        {
                            // type has moved namespaces
                            if (!oldProperty.TypeName.InSameNamespaceAs(newProperty.TypeName)
                                && oldProperty.TypeName.Name == newProperty.TypeName.Name)
                            {
                                typeDifference.IdentifiedChange(
                                    new BinaryTypeChange(
                                        TypeChangeReason.PropertyChanged,
                                        oldProperty,
                                        newProperty,
                                        string.Format(
                                            "Property changed namespace from {0} to {1}",
                                            oldProperty.TypeName,
                                            newProperty.TypeName
                                            )
                                    ));
                            }
                            else
                            {
                                typeDifference.IdentifiedChange(
                                    new BinaryTypeChange(
                                        TypeChangeReason.PropertyChanged,
                                        oldProperty,
                                        newProperty,
                                        string.Format(
                                            "Property changed type from {0} to {1}",
                                            oldProperty.TypeName,
                                            newProperty.TypeName
                                            )
                                    ));
                            }
                        }
                    }
                }
			}
		}
	}
}
